/**
 * Copyright (c) 2019 xstgongyi.org
 * All rights reserved.
 */
package org.xstgongyi.eduaid.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.xstgongyi.eduaid.bo.*;
import org.xstgongyi.eduaid.controller.StudentAidDTO;
import org.xstgongyi.eduaid.controller.StudentAidListParamDTO;
import org.xstgongyi.eduaid.dao.StudentAidMapper;
import org.xstgongyi.eduaid.dataobject.*;
import org.xstgongyi.eduaid.manager.GradeManager;
import org.xstgongyi.eduaid.manager.ProjectManager;
import org.xstgongyi.eduaid.manager.SemesterManager;
import org.xstgongyi.eduaid.manager.StudentManager;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author oldcrane<br       />
 * @created 2019-04-24
 */
@Service
public class StudentAidServiceImpl implements StudentAidService {

    @Autowired
    private StudentAidMapper aidDao;

    @Autowired
    private ProjectManager projectManager;

    @Autowired
    private SemesterManager semesterManager;

    @Autowired
    private GradeManager gradeManager;

    @Autowired
    private StudentManager studentManager;

    @Override
    public StudentAidDO getById(Integer id) {
        return aidDao.getById(id);
    }

    private void putIntoMapIfNotEmpty(Map<String, Object> map, String key, String value) {
        if (null != value && value.length() > 0) {
            map.put(key, value);
        }
    }

    private Map<String, Object> assembleCondition(StudentAidListParamDTO paramDTO, boolean isPaged) {
        Map<String, Object> condition = new HashMap<>();
        if (null != paramDTO.getProjectId()) {
            condition.put("projectId", paramDTO.getProjectId());
        }
        if (null != paramDTO.getSemesterId()) {
            condition.put("semesterId", paramDTO.getSemesterId());
        }
        if (null != paramDTO.getSponsorId()) {
            condition.put("sponsorId", paramDTO.getSponsorId());
        }
        if (null != paramDTO.getStudentId()) {
            condition.put("studentId", paramDTO.getStudentId());
        }
        putIntoMapIfNotEmpty(condition, "aidStatus", paramDTO.getAidStatus());
        putIntoMapIfNotEmpty(condition, "aidType", paramDTO.getAidType());
        putIntoMapIfNotEmpty(condition, "pairStatus", paramDTO.getPairStatus());
        putIntoMapIfNotEmpty(condition, "donateStatus", paramDTO.getDonateStatus());
        putIntoMapIfNotEmpty(condition, "fundStatus", paramDTO.getFundStatus());
        putIntoMapIfNotEmpty(condition, "receiptStatus", paramDTO.getReceiptStatus());
        putIntoMapIfNotEmpty(condition, "summaryStatus", paramDTO.getSummaryStatus());
        putIntoMapIfNotEmpty(condition, "receiptFeedbackStatus", paramDTO.getReceiptFeedbackStatus());
        putIntoMapIfNotEmpty(condition, "summaryFeedbackStatus", paramDTO.getSummaryFeedbackStatus());

        if (isPaged) {
            int limit = (null == paramDTO.getLimit()) ? 20 : paramDTO.getLimit();
            limit = limit <= 0 ? 20 : limit;

            int total = null == paramDTO.getTotal() ? 0 : paramDTO.getTotal();
            int page = null == paramDTO.getPage() ? 1 : paramDTO.getPage().intValue();
            page = ((page - 1) * limit > total) ? 1 : page;
            page = page < 1 ? 1 : page;
            paramDTO.setPage(page);

            condition.put("offset", (page - 1) * limit);
            condition.put("limit", limit);
        }

        return condition;
    }

    @Override
    public List<StudentAidDO> listPage(StudentAidListParamDTO paramDTO) {
        Map<String, Object> condition = assembleCondition(paramDTO, true);
        return aidDao.listPage(condition);
    }

    @Override
    public int countTotal(StudentAidListParamDTO paramDTO) {
        Map<String, Object> condition = assembleCondition(paramDTO, false);
        int total = aidDao.countTotal(condition);
        return total;
    }

    @Override
    public void insert(StudentAidDO aid) {
        LocalDateTime now = LocalDateTime.now();
        aid.setGmtCreated(now);
        aid.setGmtUpdated(now);
        aidDao.insert(aid);
    }

    @Override
    public void update(StudentAidDO aid) {
        aid.setGmtUpdated(LocalDateTime.now());
        aidDao.update(aid);
    }

    @Override
    public void updateAidTraceInfo(StudentAidDTO dto) {
        StudentAidDO aid = aidDao.getById(dto.getId());

        if (null != dto.getSchool()) {
            aid.setSchool(dto.getSchool());
        }
        if (null != dto.getGradeId()) {
            aid.setGradeId(dto.getGradeId());
        }

        // TODO 如果金额变化，并且已发放助学金，需要增加财务处理逻辑
        // TODO 资助金额改变，低频，建议把资助金额变更和各状态、资助人信息修改分开为两个操作。
        if (null != dto.getAidAmount()) {
            aid.setAidAmount(dto.getAidAmount());
        }

        if (null != dto.getAidStatus()) {
            aid.setAidStatus(dto.getAidStatus());
        }
        if (null != dto.getAidType()) {
            aid.setAidType(dto.getAidType());
        }
        if (null != dto.getPairStatus()) {
            aid.setPairStatus(dto.getPairStatus());
        }
        // TODO 如果资助人已捐款，变更资助人，需要增加财务处理逻辑。
        // TODO 资助人变更，低频，建议设立一个单独的操作
        if (null != dto.getSponsorId()) {
            aid.setSponsorId(dto.getSponsorId());
        }
        if (null != dto.getVolunteerId()) {
            aid.setVolunteerId(dto.getVolunteerId());
        }
        if (null != dto.getDonateStatus()) {
            aid.setDonateStatus(dto.getDonateStatus());
        }
        if (null != dto.getFundStatus()) {
            aid.setFundStatus(dto.getFundStatus());
        }
        if (null != dto.getReceiptStatus()) {
            aid.setReceiptStatus(dto.getReceiptStatus());
        }
        if (null != dto.getReceiptFeedbackStatus()) {
            aid.setReceiptFeedbackStatus(dto.getReceiptFeedbackStatus());
        }
        if (null != dto.getSummaryStatus()) {
            aid.setSummaryStatus(dto.getSummaryStatus());
        }
        if (null != dto.getSummaryFeedbackStatus()) {
            aid.setSummaryFeedbackStatus(dto.getSummaryFeedbackStatus());
        }
        if (null != dto.getRemark()) {
            aid.setRemark(dto.getRemark());
        }

        aidDao.update(aid);
    }

    @Override
    public void insertAidForNewStudent(StudentDO student) {
        StudentAidDO aid = new StudentAidDO();
        aid.setStudentId(student.getId());
        aid.setSemesterId(student.getRegSemesterId());
        aid.setProjectId(student.getProjectId());

        aid.setSchool(student.getRegSchool());
        aid.setGradeId(student.getRegGradeId());
        aid.setAidStatus(StudentAidStatus.approved);// 缺省为确认资助
        aid.setAidType(StudentAidType.targeted); // 缺省为一对一资助
        aid.setAidAmount(null == student.getSuggestAmount() ? 0 : student.getSuggestAmount() / 2);
        aid.setPairStatus(StudentPairStatus.waitPair);
        aid.setFundStatus(FundStatus.waitTransfer);
        aid.setReceiptStatus(StudentFeedbackStatus.waitFeedback);
        aid.setSummaryStatus(StudentFeedbackStatus.waitFeedback);
        aid.setReceiptFeedbackStatus(SponsorFeedbackStatus.todo);
        aid.setSummaryFeedbackStatus(SponsorFeedbackStatus.todo);
        LocalDateTime now = LocalDateTime.now();
        aid.setGmtCreated(now);
        aid.setGmtUpdated(now);
        aidDao.insert(aid);
    }

    @Override
    public int extendAid(Integer projectId, Integer nextSemesterId) {
        // 检查项目是否存在
        ProjectDO project = projectManager.getById(projectId);
        if (null == project) {
            return 0;
        }

        // 获取上个学期，检查当前学期是否存在，检查两者是否属于同一个年级
        int semesterId = calcPrevSemester(nextSemesterId);
        SemesterDO curSemester = semesterManager.getById(semesterId);
        SemesterDO nextSemester = semesterManager.getById(nextSemesterId);
        if (null == curSemester || null == nextSemester) {
            return 0;
        }

        // TODO 检查下个学期是否开启。

        List<StudentAidDO> aidList = listUnextendedAid(projectId, semesterId, nextSemesterId);
        int rowNum = 0;

        // 如果下个学期跟本学期属于同一个学年
        // 为所有本学期得到资助的同学创建下学期的资助记录
        // 确认资助、金额同上学期、资助类型同上学期、资助人同上学期、资助人设为已打款。打款、收据、小结、反馈，初始化。
        boolean sameSchoolYear = curSemester.getSchoolYearId().equals(nextSemester.getSchoolYearId());
        if (sameSchoolYear) {
            for (StudentAidDO aid : aidList) {
                if (StudentAidStatus.funded != aid.getAidStatus()) {
                    continue; // 如果本学期该学生未完成资助，跳过
                }
                StudentAidDO nextAid = new StudentAidDO();
                nextAid.setProjectId(aid.getProjectId());
                nextAid.setSemesterId(nextSemesterId);
                nextAid.setAidAmount(aid.getAidAmount());
                nextAid.setAidStatus(StudentAidStatus.approved);
                nextAid.setAidType(aid.getAidType());
                nextAid.setDonateStatus(aid.getDonateStatus());
                nextAid.setFundStatus(FundStatus.waitTransfer);
                nextAid.setGradeId(aid.getGradeId());
                nextAid.setPairStatus(aid.getPairStatus());
                nextAid.setReceiptFeedbackStatus(SponsorFeedbackStatus.todo);
                nextAid.setReceiptStatus(StudentFeedbackStatus.waitFeedback);
                nextAid.setSchool(aid.getSchool());
                nextAid.setSponsorId(aid.getSponsorId());
                nextAid.setStudentId(aid.getStudentId());
                nextAid.setSummaryFeedbackStatus(SponsorFeedbackStatus.todo);
                nextAid.setSummaryStatus(StudentFeedbackStatus.waitFeedback);
                nextAid.setVolunteerId(aid.getVolunteerId());

                this.insert(nextAid);
                rowNum++;
            }

            // 如果下个学期属于新的学年
            // 只为本学期得到资助的、非毕业班、非大学的同学创建下学期的资助记录
            // 确认资助，金额同上学期，资助类型同上学期、资助人同上学期，结对状态人设为"已确认待捐款"，其他状态均初始化。
        } else {
            for (StudentAidDO aid : aidList) {
                if (StudentAidStatus.funded != aid.getAidStatus()) {
                    continue; // 如果本学期该学生未完成资助，跳过
                }
                GradeDO grade = gradeManager.getById(aid.getGradeId());
                if (grade.getStage() == EducationStage.college || grade.getGraduateGrade() == true) {
                    continue; // 大学生或毕业班，跳过
                }
                StudentAidDO nextAid = new StudentAidDO();
                nextAid.setProjectId(aid.getProjectId());
                nextAid.setSemesterId(nextSemesterId);
                nextAid.setSchool(aid.getSchool());
                nextAid.setGradeId(grade.getNextGradeId());
                nextAid.setAidAmount(aid.getAidAmount());
                nextAid.setAidStatus(StudentAidStatus.approved);

                nextAid.setAidType(aid.getAidType());
                nextAid.setSponsorId(aid.getSponsorId());
                nextAid.setPairStatus(nextAid.getAidType() == StudentAidType.targeted ? StudentPairStatus.confirmed
                        : StudentPairStatus.noNeed);

                nextAid.setFundStatus(FundStatus.waitTransfer);
                nextAid.setReceiptFeedbackStatus(SponsorFeedbackStatus.todo);
                nextAid.setReceiptStatus(StudentFeedbackStatus.waitFeedback);
                nextAid.setStudentId(aid.getStudentId());
                nextAid.setSummaryFeedbackStatus(SponsorFeedbackStatus.todo);
                nextAid.setSummaryStatus(StudentFeedbackStatus.waitFeedback);
                nextAid.setVolunteerId(aid.getVolunteerId());

                this.insert(nextAid);
                rowNum++;
            }
        }

        // 返回新创建的学生资助记录的数量
        return rowNum;
    }

    // 获取本学期的名单中，尚未生成下学期的资助信息的学生名单
    private List<StudentAidDO> listUnextendedAid(Integer projectId, Integer curSemesterId, Integer nextSemesterId) {
        Map<String, Object> condition = new HashMap<>();
        condition.put("projectId", projectId);
        condition.put("curSemesterId", curSemesterId);
        condition.put("nextSemesterId", nextSemesterId);
        List<StudentAidDO> aidList = aidDao.listForExtend(condition);
        return aidList;
    }

    // 计算上一个学期
    private Integer calcPrevSemester(Integer curSemesterId) {
        if (null != curSemesterId) {
            return curSemesterId - 1; // 是否应修改为根据配置计算？TBD
        } else {
            return null;
        }
    }

    @Override
    public List<StudentDO> listUnextendedStudent(Integer projectId, Integer semesterId) {
        Integer prevSemesterId = calcPrevSemester(semesterId);
        List<StudentAidDO> aidList = listUnextendedAid(projectId, prevSemesterId, semesterId);

        List<StudentDO> studentList = new ArrayList<StudentDO>();
        for (StudentAidDO aid : aidList) {
            StudentDO student = studentManager.getById(aid.getStudentId());
            studentList.add(student);
        }
        return studentList;
    }

    @Override
    public int insertAidForExistingStudent(StudentAidDTO aidDTO) {
        // 先检查该学生在指定的学期是否已存在资助记录。如果存在，中止处理
        Map<String, Object> condition = new HashMap<>();
        condition.put("projectId", aidDTO.getProjectId());
        condition.put("semesterId", aidDTO.getSemesterId());
        condition.put("studentId", aidDTO.getStudentId());
        condition.put("offset", 0);
        condition.put("limit", 9999);

        List<StudentAidDO> existAids = aidDao.listPage(condition);
        if (existAids.size() > 0) {
            // 助学记录已经存在，不能重复创建
            return 0;
        }

        StudentAidDO aidDO = aidDTO.toStudentAidDO();
        if (aidDO.getAidStatus() == null) {
            aidDO.setAidStatus(StudentAidStatus.approved);
        }
        if (aidDO.getFundStatus() == null) {
            aidDO.setFundStatus(StudentAidStatus.funded == aidDO.getAidStatus() ? FundStatus.transfered : FundStatus.waitTransfer);
        }
        if (aidDO.getAidType() == null) {
            aidDO.setAidType(StudentAidType.targeted);
        }
        if (aidDO.getDonateStatus() == null) {
            aidDO.setDonateStatus(StudentAidType.nontarget == aidDO.getAidType() ? DonateStatus.noNeed : DonateStatus.waitDonate);
        }
        if (aidDO.getReceiptStatus() == null) {
            aidDO.setReceiptStatus(StudentFeedbackStatus.waitFeedback);
        }
        if (aidDO.getSummaryStatus() == null) {
            aidDO.setSummaryStatus(StudentFeedbackStatus.waitFeedback);
        }
        if (aidDO.getReceiptFeedbackStatus() == null) {
            aidDO.setReceiptFeedbackStatus(SponsorFeedbackStatus.todo);
        }
        if (aidDO.getSummaryFeedbackStatus() == null) {
            aidDO.setSummaryFeedbackStatus(SponsorFeedbackStatus.todo);
        }
        this.insert(aidDO);
        return 1;

    }

    @Override
    public int batchUpdate(int[] aidIds, String action) {
        action = null == action ? "" : action.trim();
        if (null == aidIds || 0 == aidIds.length || action.isEmpty()) {
            return 0;
        }

        Map<String, Object> condition = new HashMap<>();
        condition.put("aidIds", aidIds);
        switch (action.trim()) {
            case "batchTransferring":
                condition.put("aidStatus", StudentAidStatus.transferring);
                break;
            case "batchFunded":
                condition.put("aidStatus", StudentAidStatus.funded);
                condition.put("fundStatus", FundStatus.transfered);
                break;
            case "batchDonated":
                condition.put("pairStatus", StudentPairStatus.donated);
                break;
            case "batchReceiptReceived":
                condition.put("receiptStatus", StudentFeedbackStatus.collected);
                break;
            case "batchReceiptFeedback":
                condition.put("receiptFeedbackStatus", SponsorFeedbackStatus.completed);
                break;
            case "batchSummaryReceived":
                condition.put("summaryStatus", StudentFeedbackStatus.collected);
                break;
            case "batchSummaryFeedback":
                condition.put("summaryFeedbackStatus", SponsorFeedbackStatus.completed);
                break;
        }
        condition.put("gmtUpdated", LocalDateTime.now());
        return aidDao.batchUpdateStatus(condition);
    }

}
